package com.gitee.fivefaith.hikpreview.component.hik;

import static com.gitee.fivefaith.hikpreview.component.hik.ClientDemo.hCNetSDK;
import static com.gitee.fivefaith.hikpreview.component.hik.ClientDemo.playControl;

import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import lombok.extern.slf4j.Slf4j;

/**
 * 视频取流预览，下载，抓图mok
 * 
 * @author ZhaoAnchi
 * @version 1.0
 * @since 2025-08-07
 */
@SuppressWarnings({"AlibabaLowerCamelCaseVariableNaming", "DuplicatedCode", "LoggingSimilarMessage", "OctalInteger",
    "ResultOfMethodCallIgnored"})
@Slf4j
public class VideoDemo {
    // 下载用定时器
    Timer downloadtimer;
    // 回放用定时器
    Timer Playbacktimer;
    // 预览回调函数实现
    static FRealDataCallBack fRealDataCallBack;
    // 裸码流回调函数
    static fPlayEScallback fPlayescallback;
    // 回放码流回调
    static playDataCallBack playBackCallBack;
    // 标准码流回调
    static fStdDataCallBack fStdDataCallback;
    static int Count = 0;
    int m_lLoadHandle;
    // 回放句柄
    int iPlayBack;
    static File file;
    static File esRealDatafile;
    static boolean palybackFlay = false;
    static FileOutputStream outputStream;
    static IntByReference m_lPort = new IntByReference(-1);
    static FileOutputStream EstremDataoutputStream = null;
    static int fileLenth = 0;

    /**
     * @param userID 设备登录句柄
     * @param iChannelNo 通道号
     * @return 取流预览句柄
     */
    public static int getRealStreamData(int userID, int iChannelNo) {
        if (userID == -1) {
            log.info("请先注册");
            return -1;
        }
        HCNetSDK.NET_DVR_PREVIEWINFO previewInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        previewInfo.read();
        // 窗口句柄，从回调取流不显示一般设置为空
        previewInfo.hPlayWnd = null;
        // 通道号
        previewInfo.lChannel = iChannelNo;
        // 0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        previewInfo.dwStreamType = 0;
        // 连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输）
        // ，7- RTSP/HTTPS，8- NPQ
        previewInfo.dwLinkMode = 0;
        // 0- 非阻塞取流，1- 阻塞取流
        previewInfo.bBlocked = 1;
        // 应用层取流协议：0- 私有协议，1- RTSP协议
        previewInfo.byProtoType = 0;
        previewInfo.write();
        // 回调函数定义必须是全局的
        if (fRealDataCallBack == null) {
            fRealDataCallBack = new FRealDataCallBack();
        }
        // 开启预览
        int Handle = hCNetSDK.NET_DVR_RealPlay_V40(userID, previewInfo, fRealDataCallBack, null);
        if (Handle == -1) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.error("取流失败:{}", iErr);
            return -1;
        }
        log.info("取流成功");
        return Handle;
    }

    /**
     * 停止取流
     *
     * @param PlayHandle 预览句柄
     */
    public static void stopRealStreamData(int PlayHandle) {
        if (PlayHandle == -1) {
            log.info("实时取流未开启，请先开启实时取流");
            return;
        }
        if (!hCNetSDK.NET_DVR_StopRealPlay(PlayHandle)) {
            log.error("停止取流失败，err:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        log.info("停止取流成功");
    }

    /**
     * 获取实时裸码流回调数据
     *
     * @param userID 登录句柄
     * @param iChannelNo 通道号参数
     */
    public static int getESRealStreamData(int userID, int iChannelNo) {
        if (userID == -1) {
            log.info("请先注册");
            return -1;
        }
        String resultFileName = System.getProperty("user.dir") + "\\Download\\ESRealStreamData.data";
        esRealDatafile = new File(resultFileName);
        if (!esRealDatafile.exists()) {
            try {
                esRealDatafile.createNewFile();
            } catch (Exception e) {
                log.error("创建文件失败", e);
            }
        }
        try {
            EstremDataoutputStream = new FileOutputStream(esRealDatafile);
        } catch (FileNotFoundException e) {
            log.error("文件未找到", e);
        }
        HCNetSDK.NET_DVR_PREVIEWINFO previewInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        previewInfo.read();
        // 窗口句柄，从回调取流不显示一般设置为空
        previewInfo.hPlayWnd = null;
        // 通道号
        previewInfo.lChannel = iChannelNo;
        // 0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        previewInfo.dwStreamType = 0;
        // 连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输）
        // ，7- RTSP/HTTPS，8- NPQ
        previewInfo.dwLinkMode = 0;
        // 0- 非阻塞取流，1- 阻塞取流
        previewInfo.bBlocked = 1;
        // 应用层取流协议：0- 私有协议，1- RTSP协议
        previewInfo.byProtoType = 0;
        previewInfo.write();
        // 开启预览
        int Handle = hCNetSDK.NET_DVR_RealPlay_V40(userID, previewInfo, null, null);
        if (Handle == -1) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.error("取流失败:{}", iErr);
            return -1;
        }
        log.info("取流成功");

        // 设置裸码流回调函数
        if (fPlayescallback == null) {
            fPlayescallback = new fPlayEScallback();
        }
        if (!hCNetSDK.NET_DVR_SetESRealPlayCallBack(Handle, fPlayescallback, null)) {
            log.error("设置裸码流回调失败，错误码：{}", hCNetSDK.NET_DVR_GetLastError());
        }

        // 设置RTP回调函数
        // if (fStdDataCallback==null)
        // {
        // fStdDataCallback=new fStdDataCallBack();
        // }
        // boolean setRtpcallback=hCNetSDK.NET_DVR_SetStandardDataCallBack(lPlay,fStdDataCallback,0);
        // if (setRtpcallback==false)
        // {
        // log.info("设置RTP回调失败，错误码："+hCNetSDK.NET_DVR_GetLastError());
        // }

        // 预览一段时间；如果要一直取流预览，需要保证程序一直运行
        /*       
        try {
          Thread.sleep(5000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        
        
        String path = ".\\savevideo.mp4";
        Boolean bSaveVideo = hCNetSDK.NET_DVR_SaveRealData_V30(lPlay,0x2,path);
        if (bSaveVideo == false) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.info("NET_DVR_SaveRealData_V30 failed" + iErr);
            return;
        }
        log.info("NET_DVR_SaveRealData_V30 suss");
        
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        Boolean bStopSaveVideo = hCNetSDK.NET_DVR_StopSaveRealData(lPlay);
        if (bStopSaveVideo == false) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.info("NET_DVR_StopSaveRealData failed" + iErr);
            return;
        }
            log.info("NET_DVR_StopSaveRealData suss");
        
        
        if (lPlay>=0) {
            if (hCNetSDK.NET_DVR_StopRealPlay(lPlay))
            {
                log.info("停止预览成功");
                return;
            }
        }*/
        return Handle;
    }

    /**
     * 开始录像，开启录像需要先开启实时预览
     * 
     * @param playHandle playHandle
     */
    public static void startSaveRealData(int playHandle) {
        if (playHandle == -1) {
            log.info("实时取流未开启，请先开启实时取流");
            return;
        }
        String path = System.getProperty("user.dir") + "\\Download\\SaveRealData.mp4";
        byte[] byPath = new byte[0];
        try {
            byPath = path.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            log.error("编码错误", e);
        }
        HCNetSDK.BYTE_ARRAY byArray = new HCNetSDK.BYTE_ARRAY(byPath.length);
        System.arraycopy(byPath, 0, byArray.byValue, 0, byPath.length);
        byArray.write();
        boolean bSaveVideo = hCNetSDK.NET_DVR_SaveRealData_V30(playHandle, 0x2, byArray.getPointer());
        if (!bSaveVideo) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.error("NET_DVR_SaveRealData_V30 failed{}", iErr);
            return;
        }
        log.info("NET_DVR_SaveRealData_V30 suss");
    }

    /**
     * 停止录像
     * 
     * @param playHandle playHandle
     */
    public static void stopSaveRealData(int playHandle) {
        if (playHandle == -1) {
            log.info("实时取流未开启，请先开启实时取流");
            return;
        }
        if (!hCNetSDK.NET_DVR_StopSaveRealData(playHandle)) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.info("NET_DVR_StopSaveRealData failed {}", iErr);
            return;
        }
        log.info("NET_DVR_StopSaveRealData suss");

    }

    /**
     * 播放库抓图
     */
    public static void getPicbyPlayCtrl() {

        // 取流成功后，延时一段时间保证播放库解码开始
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            log.error("InterruptedException", e);
        }
        IntByReference pWidth = new IntByReference(0);
        IntByReference pHieght = new IntByReference(0);
        boolean bFlag = playControl.PlayM4_GetPictureSize(m_lPort.getValue(), pWidth, pHieght);
        if (!bFlag) {
            log.info("获取失败：{}", playControl.PlayM4_GetLastError(m_lPort.getValue()));
        }
        log.info(String.valueOf(pWidth.getValue()));
        log.info(String.valueOf(pHieght.getValue()));
        IntByReference RealPicSize = new IntByReference(0);
        int picsize = pWidth.getValue() * pHieght.getValue() * 5;
        HCNetSDK.BYTE_ARRAY picByte = new HCNetSDK.BYTE_ARRAY(picsize);
        picByte.write();
        Pointer pByte = picByte.getPointer();
        boolean b_GetPic = playControl.PlayM4_GetJPEG(m_lPort.getValue(), pByte, picsize, RealPicSize);
        if (!b_GetPic) {
            log.info("抓图失败：{}", playControl.PlayM4_GetLastError(m_lPort.getValue()));
        }
        picByte.read();
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
        String newName = sf.format(new Date());
        FileOutputStream fout;
        try {

            fout = new FileOutputStream(System.getProperty("user.dir") + "//pic//" + newName + ".jpg");
            // 将字节写入文件
            long offset = 0;
            ByteBuffer buffers = pByte.getByteBuffer(offset, RealPicSize.getValue());
            byte[] bytes = new byte[RealPicSize.getValue()];
            buffers.rewind();
            buffers.get(bytes);
            fout.write(bytes);
            fout.close();
        } catch (FileNotFoundException e) {
            log.error("FileNotFoundException", e);
        } catch (IOException e) {
            log.error("IOException", e);
        }
        log.info("抓图成功!");

    }

    /**
     * 按时间回放录像
     * 
     * @param userID 设备登录句柄
     * @param lChannel 通道号
     */
    @SuppressWarnings("ResultOfMethodCallIgnored")
    public void playBackBytime(int userID, int lChannel) {
        // 保存回调函数的码流数据
        file = new File(System.getProperty("user.dir") + "\\Download\\Videodatabytime.mp4");
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (Exception e) {
                log.error("Exception", e);
            }
        }
        try {
            outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            log.error("FileNotFoundException", e);
        }
        HCNetSDK.NET_DVR_VOD_PARA net_dvr_vod_para = new HCNetSDK.NET_DVR_VOD_PARA();
        net_dvr_vod_para.dwSize = net_dvr_vod_para.size();
        // 通道号
        net_dvr_vod_para.struIDInfo.dwChannel = lChannel;
        // 开始时间
        net_dvr_vod_para.struBeginTime.dwYear = 2024;
        net_dvr_vod_para.struBeginTime.dwMonth = 8;
        net_dvr_vod_para.struBeginTime.dwDay = 5;
        net_dvr_vod_para.struBeginTime.dwHour = 10;
        net_dvr_vod_para.struBeginTime.dwMinute = 00;
        net_dvr_vod_para.struBeginTime.dwSecond = 00;
        // 停止时间
        net_dvr_vod_para.struEndTime.dwYear = 2024;
        net_dvr_vod_para.struEndTime.dwMonth = 8;
        net_dvr_vod_para.struEndTime.dwDay = 5;
        net_dvr_vod_para.struEndTime.dwHour = 10;
        net_dvr_vod_para.struEndTime.dwMinute = 05;
        net_dvr_vod_para.struEndTime.dwSecond = 00;
        // 回放的窗口句柄，若置为空，SDK仍能收到码流数据，但不解码显示
        net_dvr_vod_para.hWnd = null;
        net_dvr_vod_para.write();
        int iPlayBack = hCNetSDK.NET_DVR_PlayBackByTime_V40(userID, net_dvr_vod_para);
        if (iPlayBack <= -1) {
            log.info("按时间回放失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            palybackFlay = true;
            return;
        }
        // 开启取流
        IntByReference intP = new IntByReference(54962 * 1024);
        IntByReference intInlen1 = new IntByReference(0);
        boolean bCrtl = hCNetSDK.NET_DVR_PlayBackControl_V40(iPlayBack, HCNetSDK.NET_DVR_PLAYSTART, Pointer.NULL, 0,
            Pointer.NULL, intInlen1);
        if (!bCrtl) {
            log.info("NET_DVR_PLAYSTART失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        // 切换倒放
        // IntByReference intInlen2=new IntByReference(0);
        // boolean bCrtl2=hCNetSDK.NET_DVR_PlayBackControl_V40(iPlayBack, HCNetSDK.NET_DVR_PLAY_REVERSE, Pointer.NULL,
        // 0, Pointer.NULL,intInlen2);
        // if (bCrtl2==false)
        // {
        // log.info("NET_DVR_PLAY_REVERSE失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
        // return;
        // }

        if (playBackCallBack == null) {
            playBackCallBack = new playDataCallBack();
        }
        boolean bRet = hCNetSDK.NET_DVR_SetPlayDataCallBack_V40(iPlayBack, playBackCallBack, Pointer.NULL);
        // 开始计时器
        // 新建定时器
        Playbacktimer = new Timer();
        // 0秒后开始响应函数
        Playbacktimer.schedule(new PlaybackTask(), 0, 5000);
    }

    /**
     * 按文件回放录像
     * 
     * @param userID 用户ID
     * @param lChannel 通道号
     */
    @SuppressWarnings("BusyWait")
    public static void playBackByfile(int userID, int lChannel) {
        // 保存回调函数的码流数据
        file = new File(System.getProperty("user.dir") + "\\Download\\Videodatabyfile.mp4");

        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (Exception e) {
                log.error("Exception", e);
            }
        }
        try {
            outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            log.error("FileNotFoundException", e);
        }
        String strFileName = "";
        HCNetSDK.NET_DVR_FILECOND_V40 struFileCond = new HCNetSDK.NET_DVR_FILECOND_V40();
        struFileCond.read();
        // 通道号 NVR设备路数小于32路的起始通道号从33开始，依次增加
        struFileCond.lChannel = lChannel;
        // 录象文件类型 0=定时录像
        struFileCond.byFindType = 0;
        // 起始时间
        struFileCond.struStartTime.dwYear = 2023;
        struFileCond.struStartTime.dwMonth = 9;
        struFileCond.struStartTime.dwDay = 8;
        struFileCond.struStartTime.dwHour = 8;
        struFileCond.struStartTime.dwMinute = 00;
        struFileCond.struStartTime.dwSecond = 00;
        // 停止时间
        struFileCond.struStopTime.dwYear = 2023;
        struFileCond.struStopTime.dwMonth = 9;
        struFileCond.struStopTime.dwDay = 8;
        struFileCond.struStopTime.dwHour = 10;
        struFileCond.struStopTime.dwMinute = 30;
        struFileCond.struStopTime.dwSecond = 00;
        struFileCond.write();
        int FindFileHandle = hCNetSDK.NET_DVR_FindFile_V40(userID, struFileCond);
        if (FindFileHandle <= -1) {
            log.info("查找建立失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
        }

        while (true) {
            HCNetSDK.NET_DVR_FINDDATA_V40 struFindData = new HCNetSDK.NET_DVR_FINDDATA_V40();

            long State = hCNetSDK.NET_DVR_FindNextFile_V40(FindFileHandle, struFindData);
            if (State <= -1) {

                log.info("查找失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
                return;

            }
            // 获取文件信息成功
            else if (State == 1000) {
                struFindData.read();
                strFileName = new String(struFindData.sFileName, StandardCharsets.UTF_8).trim();
                log.info("文件名称：{}", strFileName);
                log.info("文件大小: {}", struFindData.dwFileSize);
                log.info("获取文件成功");
                break;
            }
            // 未查找到文件
            else if (State == 1001) {
                log.info("未查找到文件");
                break;
            }
            // 正在查找请等待
            else if (State == 1002) {
                log.info("正在查找，请等待");
                continue;
            }
            // 没有更多的文件，查找结束
            else if (State == 1003) {
                log.info("没有更多的文件，查找结束");
                break;
            }
            // 查找文件时异常
            else if (State == 1004) {
                log.info("没有更多的文件，查找结束");
                break;
            }
            // 查找文件超时
            else if (State == 1005) {
                log.info("没有更多的文件，查找结束");
                break;
            }

        }
        boolean b_CloseHandle = hCNetSDK.NET_DVR_FindClose_V30(FindFileHandle);
        if (!b_CloseHandle) {
            log.info("关闭失败，错误码为: {}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }

        int lPlayByFileHandle = hCNetSDK.NET_DVR_PlayBackByName(userID, strFileName, null);
        if (lPlayByFileHandle <= -1) {
            log.info("按文件回放失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        IntByReference intP1 = new IntByReference(0);
        IntByReference intInlen = new IntByReference(0);
        boolean b_PlayBackStart = hCNetSDK.NET_DVR_PlayBackControl_V40(lPlayByFileHandle, HCNetSDK.NET_DVR_PLAYSTART,
            intP1.getPointer(), 4, Pointer.NULL, intInlen);
        if (!b_PlayBackStart) {
            log.info("开始播放失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        if (playBackCallBack == null) {
            playBackCallBack = new playDataCallBack();
        }
        boolean bRet = hCNetSDK.NET_DVR_SetPlayDataCallBack_V40(lPlayByFileHandle, playBackCallBack, Pointer.NULL);
        while (true) {
            int Pos = hCNetSDK.NET_DVR_GetDownloadPos(lPlayByFileHandle);
            if (Pos != 100) {
                log.info("回放进度::{}", Pos);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("InterruptedException", e);
                }
                continue;
            } else {
                break;
            }
        }
        boolean b_Stop = hCNetSDK.NET_DVR_StopPlayBack(lPlayByFileHandle);
        if (!b_Stop) {
            log.info("停止回放失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        log.info("回放成功");
    }

    // 按文件下载录像(设置转成3GP格式)
    @SuppressWarnings("BusyWait")
    public void downloadRecordByFile(int userID, int iChannelNo) {
        String strFileName = "";
        HCNetSDK.NET_DVR_FILECOND_V40 struFileCond = new HCNetSDK.NET_DVR_FILECOND_V40();
        struFileCond.read();
        // 通道号 NVR设备路数小于32路的起始通道号从33开始，依次增加
        struFileCond.lChannel = iChannelNo;
        struFileCond.dwFileType = 0Xff;
        struFileCond.byFindType = 0;
        // 起始时间
        struFileCond.struStartTime.dwYear = 2022;
        struFileCond.struStartTime.dwMonth = 11;
        struFileCond.struStartTime.dwDay = 19;
        struFileCond.struStartTime.dwHour = 12;
        struFileCond.struStartTime.dwMinute = 00;
        struFileCond.struStartTime.dwSecond = 00;
        // 停止时间
        struFileCond.struStopTime.dwYear = 2022;
        struFileCond.struStopTime.dwMonth = 11;
        struFileCond.struStopTime.dwDay = 19;
        struFileCond.struStopTime.dwHour = 15;
        struFileCond.struStopTime.dwMinute = 00;
        struFileCond.struStopTime.dwSecond = 00;
        struFileCond.write();
        int FindFileHandle = hCNetSDK.NET_DVR_FindFile_V40(userID, struFileCond);
        if (FindFileHandle <= -1) {
            log.info("查找建立失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
        }

        while (true) {
            HCNetSDK.NET_DVR_FINDDATA_V40 struFindData = new HCNetSDK.NET_DVR_FINDDATA_V40();

            long State = hCNetSDK.NET_DVR_FindNextFile_V40(FindFileHandle, struFindData);
            if (State <= -1) {

                log.info("查找失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
                return;

            }
            // 获取文件信息成功
            else if (State == 1000) {
                struFindData.read();
                strFileName = new String(struFindData.sFileName, StandardCharsets.UTF_8).trim();
                log.info("文件名称：{}", strFileName);
                log.info("文件大小: {}", struFindData.dwFileSize);
                log.info("获取文件成功");
                break;

            }
            // 未查找到文件
            else if (State == 1001) {
                log.info("未查找到文件");
                break;

            }
            // 正在查找请等待
            else if (State == 1002) {
                log.info("正在查找，请等待");
                continue;

            }
            // 没有更多的文件，查找结束
            else if (State == 1003) {
                log.info("没有更多的文件，查找结束");
                break;

            }
            // 查找文件时异常
            else if (State == 1004) {

                log.info("没有更多的文件，查找结束");
                break;

            }
            // 查找文件超时
            else if (State == 1005) {

                log.info("没有更多的文件，查找结束");
                break;

            }

        }
        boolean b_CloseHandle = hCNetSDK.NET_DVR_FindClose_V30(FindFileHandle);
        if (!b_CloseHandle) {
            log.info("关闭失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }

        // 按文件下载录像
        String SaveDir = ".\\Download\\test.mp4";

        int FileName = hCNetSDK.NET_DVR_GetFileByName(userID, strFileName, SaveDir.getBytes());
        if (FileName <= -1) {
            log.info("下载录像失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        // 转码3GP命令
        IntByReference intP = new IntByReference(5);
        IntByReference intInlen1 = new IntByReference(0);
        boolean b_PlayBack =
            hCNetSDK.NET_DVR_PlayBackControl_V40(FileName, 32, intP.getPointer(), 4, Pointer.NULL, intInlen1);
        if (!b_PlayBack) {
            log.info("转封装失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }

        IntByReference intP1 = new IntByReference(0);
        IntByReference intInlen = new IntByReference(0);
        boolean b_PlayBackStart = hCNetSDK.NET_DVR_PlayBackControl_V40(FileName, HCNetSDK.NET_DVR_PLAYSTART,
            intP1.getPointer(), 4, Pointer.NULL, intInlen);
        if (!b_PlayBackStart) {
            log.info("开始播放失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }

        /*        IntByReference intP=new IntByReference(30*1024);
        IntByReference intInlen1=new IntByReference(0);
        boolean b_PlayBack=hCNetSDK.NET_DVR_PlayBackControl_V40(FileName,24,intP.getPointer(),4, Pointer.NULL,intInlen1);
        if (!b_PlayBack) {
            log.info("设置下载速度失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
            return;
        }*/
        while (true) {
            int Pos = hCNetSDK.NET_DVR_GetDownloadPos(FileName);
            if (Pos != 100) {
                log.info("下载进度:{}", Pos);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("InterruptedException", e);
                }
                continue;
            } else {
                break;
            }
        }
        boolean b_Stop = hCNetSDK.NET_DVR_StopGetFile(FileName);
        if (!b_Stop) {
            log.info("停止下载失败，错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            return;
        }
        log.info("下载成功");
    }

    // 按时间下载录像(不支持转码3GP格式)
    public void dowmloadRecordByTime(int userID, int iChannelNo) {
        HCNetSDK.NET_DVR_PLAYCOND net_dvr_playcond = new HCNetSDK.NET_DVR_PLAYCOND();
        net_dvr_playcond.read();
        // 通道号 NVR设备路数小于32路的起始通道号从33开始，依次增加
        net_dvr_playcond.dwChannel = iChannelNo;
        // 开始时间
        net_dvr_playcond.struStartTime.dwYear = 2023;
        net_dvr_playcond.struStartTime.dwMonth = 9;
        net_dvr_playcond.struStartTime.dwDay = 14;
        net_dvr_playcond.struStartTime.dwHour = 12;
        net_dvr_playcond.struStartTime.dwMinute = 45;
        net_dvr_playcond.struStartTime.dwSecond = 11;
        // 停止时间
        net_dvr_playcond.struStopTime.dwYear = 2023;
        net_dvr_playcond.struStopTime.dwMonth = 9;
        net_dvr_playcond.struStopTime.dwDay = 14;
        net_dvr_playcond.struStopTime.dwHour = 12;
        net_dvr_playcond.struStopTime.dwMinute = 45;
        net_dvr_playcond.struStopTime.dwSecond = 19;
        net_dvr_playcond.write();
        String sFileName = ".\\Download\\" + System.currentTimeMillis() + ".mp4";
        log.info(sFileName);
        m_lLoadHandle = hCNetSDK.NET_DVR_GetFileByTime_V40(userID, sFileName, net_dvr_playcond);
        if (m_lLoadHandle >= 0) {
            hCNetSDK.NET_DVR_PlayBackControl(m_lLoadHandle, HCNetSDK.NET_DVR_PLAYSTART, 0, null);
            /*
            IntByReference intP=new IntByReference(5*8*1024);
            IntByReference intInlen=new IntByReference(0);
            boolean b_PlayBack=ClientDemo.hCNetSDK.NET_DVR_PlayBackControl_V40(m_lLoadHandle,24,intP.getPointer(),4,Pointer.NULL,intInlen);
            if (!b_PlayBack) {
                log.info("设置下载速度失败，错误码为" + ClientDemo.hCNetSDK.NET_DVR_GetLastError());
                return;
            }*/
            Date nowTime = new Date(System.currentTimeMillis());
            SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
            log.info("开始下载时间：{}", sdFormatter.format(nowTime));
            // 新建定时器
            downloadtimer = new Timer();
            // 0秒后开始响应函数
            downloadtimer.schedule(new downloadTask(), 0, 5000);
        } else {
            log.info("按时间下载失败");
            log.info("laste error {}", hCNetSDK.NET_DVR_GetLastError());
        }
    }

    /*************************************************
     * 类: DownloadTask 类描述: 下载定时器响应函数
     *************************************************/
    class downloadTask extends java.util.TimerTask {
        // 定时器函数
        @Override
        public void run() {
            IntByReference nPos = new IntByReference(0);
            hCNetSDK.NET_DVR_PlayBackControl(m_lLoadHandle, HCNetSDK.NET_DVR_PLAYGETPOS, 0, nPos);
            if (nPos.getValue() > 100) {
                m_lLoadHandle = -1;
                downloadtimer.cancel();
                log.info("由于网络原因或DVR忙,下载异常终止!");
            }
            if (nPos.getValue() == 100) {
                Date nowTime = new Date(System.currentTimeMillis());
                SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
                log.info("结束下载时间：{}", sdFormatter.format(nowTime));
                hCNetSDK.NET_DVR_StopGetFile(m_lLoadHandle);
                m_lLoadHandle = -1;
                log.info("按时间下载结束!");
                // 任务完成后，取消定时器
                // 取消任务，防止再次执行
                this.cancel();
                // 取消定时器
                if (downloadtimer != null) {
                    downloadtimer.cancel();
                }
            }
        }
    }

    /**
     * 按时间回放进度获取定时器
     */
    class PlaybackTask extends java.util.TimerTask {
        // 定时器函数
        @Override
        public void run() {
            log.info("定时器触发");
            IntByReference nPos = new IntByReference(0);

            log.info("iPlayBack {}", iPlayBack);
            boolean bret = hCNetSDK.NET_DVR_PlayBackControl(iPlayBack, HCNetSDK.NET_DVR_PLAYGETPOS, 0, nPos);
            if (bret) {
                log.info("回放进度 {}", nPos.getValue());
            } else {
                log.info("获取回放进度失败");
            }

            if (nPos.getValue() > 100) {

                hCNetSDK.NET_DVR_StopPlayBack(iPlayBack);
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        log.error("关闭文件失败", e);
                    }
                }
                palybackFlay = true;
                log.info("由于网络原因或DVR忙,回放异常终止!");
                return;
            }
            if (nPos.getValue() == 100) {
                hCNetSDK.NET_DVR_StopPlayBack(iPlayBack);
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        log.error("关闭文件失败", e);
                    }
                }
                palybackFlay = true;
                log.info("按时间回放结束");
                // 任务完成后，取消定时器
                // 取消任务，防止再次执行
                this.cancel();
                // 取消定时器
                if (Playbacktimer != null) {
                    Playbacktimer.cancel();
                }
            }
        }
    }

    // 获取IP通道
    public static void getIPChannelInfo(int userID) {
        // 获取IP接入配置参数
        IntByReference ibrBytesReturned = new IntByReference(0);
        HCNetSDK.NET_DVR_IPPARACFG_V40 m_strIpparaCfg = new HCNetSDK.NET_DVR_IPPARACFG_V40();
        m_strIpparaCfg.write();
        // lpIpParaConfig 接收数据的缓冲指针
        Pointer lpIpParaConfig = m_strIpparaCfg.getPointer();
        boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(userID, HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, lpIpParaConfig,
            m_strIpparaCfg.size(), ibrBytesReturned);
        m_strIpparaCfg.read();
        log.info("起始数字通道号：{}", m_strIpparaCfg.dwStartDChan);

        for (int iChannum = 0; iChannum < m_strIpparaCfg.dwDChanNum; iChannum++) {
            int channum = iChannum + m_strIpparaCfg.dwStartDChan;
            m_strIpparaCfg.struStreamMode[iChannum].read();
            if (m_strIpparaCfg.struStreamMode[iChannum].byGetStreamType == 0) {
                m_strIpparaCfg.struStreamMode[iChannum].uGetStream.setType(HCNetSDK.NET_DVR_IPCHANINFO.class);
                m_strIpparaCfg.struStreamMode[iChannum].uGetStream.struChanInfo.read();
                if (m_strIpparaCfg.struStreamMode[iChannum].uGetStream.struChanInfo.byEnable == 1) {
                    log.info("IP通道 {} 在线", channum);
                } else {
                    log.info("IP通道 {} 不在线", channum);
                }
            }
        }
    }

    static class fPlayEScallback implements HCNetSDK.FPlayESCallBack {

        @Override
        public void invoke(int lPreviewHandle, HCNetSDK.NET_DVR_PACKET_INFO_EX pstruPackInfo, Pointer pUser) {
            pstruPackInfo.read();
            // 将设备发送过来的回放码流数据写入文件
            // 保存I帧和P帧数据
            if (pstruPackInfo.dwPacketType == 1 || pstruPackInfo.dwPacketType == 3) {
                log.info("dwPacketType：{}：wWidth:{}：wHeight:{}：包长度：{}：帧号：{}", pstruPackInfo.dwPacketType,
                    pstruPackInfo.wWidth, pstruPackInfo.wHeight, pstruPackInfo.dwPacketSize, pstruPackInfo.dwFrameNum);
                long offset = 0;
                ByteBuffer buffers = pstruPackInfo.pPacketBuffer.getByteBuffer(offset, pstruPackInfo.dwPacketSize);
                byte[] bytes = new byte[pstruPackInfo.dwPacketSize];
                buffers.rewind();
                buffers.get(bytes);
                try {
                    EstremDataoutputStream.write(bytes);
                } catch (IOException e) {
                    log.error("写入文件失败", e);
                }
            }

        }
    }

    static class playDataCallBack implements HCNetSDK.FPlayDataCallBack {
        @Override
        public void invoke(int lPlayHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int dwUser) {
            log.info("回放码流回调...");
            // 将设备发送过来的回放码流数据写入文件
            long offset = 0;
            ByteBuffer buffers = pBuffer.getByteBuffer(offset, dwBufSize);
            byte[] bytes = new byte[dwBufSize];
            buffers.rewind();
            buffers.get(bytes);
            try {
                outputStream.write(bytes);
            } catch (IOException e) {
                log.error("写入文件失败", e);
            }
            // 播放库解码
            /*            switch (dwDataType) {
                case HCNetSDK.NET_DVR_SYSHEAD: //系统头
                    if (!playControl.PlayM4_GetPort(m_lPort)) //获取播放库未使用的通道号
                    {
                        break;
                    }
                    if (dwBufSize > 0) {
                        if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME))  //设置实时流播放模式
                        {
                            break;
                        }
                        if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打开流接口
                        {
                            break;
                        }
                        if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) //播放开始
                        {
                            break;
                        }
            
                    }
                case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
                    if ((dwBufSize > 0) && (m_lPort.getValue() != -1)) {
                        if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize))  //输入流数据
                        {
                            break;
                        }
                    }
            }*/

        }

    }

    static class fStdDataCallBack implements HCNetSDK.FStdDataCallBack {
        @Override
        public void invoke(int lPlayHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int dwUser) {
            log.info("RTP码流回调...");
        }

    }

    static class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {

        /**
         * 预览回调
         */
        @Override
        public void invoke(int lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer pUser) {
            if (Count == 100) {// 降低打印频率
                log.info("码流数据回调...dwBufSize={}", dwBufSize);
                Count = 0;
            }
            Count++;
            // 本Demo演示码流数据送人播放库解码流程,如果仅需保存实时码流数据需要自行实现，
            switch (dwDataType) {
                case HCNetSDK.NET_DVR_SYSHEAD: // 系统头
                    if (!playControl.PlayM4_GetPort(m_lPort)) // 获取播放库未使用的通道号
                    {
                        break;
                    }
                    if (dwBufSize > 0) {
                        if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME)) // 设置实时流播放模式
                        {
                            break;
                        }
                        if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) // 打开流接口
                        {
                            break;
                        }
                        if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) // 播放开始
                        {
                            break;
                        }

                    }
                case HCNetSDK.NET_DVR_STREAMDATA: // 码流数据
                    if ((dwBufSize > 0) && (m_lPort.getValue() != -1)) {
                        if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize)) // 输入流数据
                        {
                            break;
                        }
                    }
            }
        }
    }

}
